home *** CD-ROM | disk | FTP | other *** search
/ Multimedia Jumpstart / Multimedia Microsoft Jumpstart Version 1.1a (Microsoft).BIN / develpmt / source / midikeyb / midikeyb.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-09-12  |  48.6 KB  |  1,478 lines

  1. /*    (C) Copyright Microsoft Corp. 1991.  All rights reserved.
  2.  *
  3.  *    You have a royalty-free right to use, modify, reproduce and 
  4.  *    distribute the Sample Files (and/or any modified version) in 
  5.  *    any way you find useful, provided that you agree that 
  6.  *    Microsoft has no warranty obligations or liability for any 
  7.  *    Sample Application Files which are modified. 
  8.  */
  9.  
  10.  
  11. /** midikeyb.c
  12.  *
  13.  *  DESCRIPTION: 
  14.  *      This is a custom control that implements a MIDI style keyboard.
  15.  *      It's not exactly the best keyboard for a jam session; don't expect
  16.  *      to hit a bunch of hemidemisemiquavers at correct tempo...
  17.  *
  18.  *      In any case, this custom control responds to and sends MIDI short
  19.  *      messages.  This could be expanded on, but this is a good start.
  20.  *      The right mouse button is used for 'sticky keys' and will toggle
  21.  *      the state appropriately.  If you drag the pointer off of a key
  22.  *      with the left mouse button pressed, you will notice that the key
  23.  *      stays down.  This is because I do not SetCapture--and I should.
  24.  *      Another enhancement might be to add CTRL key and SHIFT key 
  25.  *      modifiers to allow key selection like a multi-select list box.
  26.  *
  27.  *      Before you can use this control, you MUST first export the window
  28.  *      procedure for the control:
  29.  *
  30.  *          EXPORTS     midiKeyBProc
  31.  *
  32.  *      You then need initialize the class before you use it:
  33.  *
  34.  *          if ( !midiKeyBInit( hInst ) )
  35.  *              die a horrible death
  36.  *          else
  37.  *              you are good to go
  38.  *
  39.  *      The colors used by the control default to black and white (black
  40.  *      for the accidental keys and white for the normal keys).  This should
  41.  *      work just dandy on all displays.
  42.  *
  43.  *      To select your own colors, you can send the KEYB_SETFGCOLOR and
  44.  *      KEYB_SETBKCOLOR messages to set the foreground (accidental) and
  45.  *      background (normal) key colors.  The lParam is the RGB() value--
  46.  *      wParam is a BOOL telling the control to repaint immediately if
  47.  *      set to TRUE.
  48.  *
  49.  *      The layout of the keyboard is set using the MIDI key values with
  50.  *      C0 being MIDI note number 0, C2 is 48 (middle C), etc.  The default
  51.  *      layout is: start = 48 (middle C), for 36 keys (3 octaves), ending
  52.  *      on 83.  I chose this by throwing darts at my MIDI poster...
  53.  *
  54.  *      You can set the layout to whatever you want by sending the 
  55.  *      KEYB_SETLAYOUT message to the control.  The HIWORD() of lParam
  56.  *      is the starting key, the LOWORD() of lParam is the number of keys.
  57.  *      wParam is a BOOL telling the control to repaint immediately if
  58.  *      set to TRUE (actually non-zero...).  You can get the current 
  59.  *      layout by sending the KEYB_GETLAYOUT message--the LONG return
  60.  *      value can be decoded as the lParam of KEYB_SETLAYOUT is encoded.
  61.  *
  62.  *      You also have the option of having labels printed on the keys
  63.  *      for MIDI note referencing.  Set the KEYBS_LABELS style flag to
  64.  *      get labels.
  65.  *      
  66.  *      The font used for the label text can be set using the standard
  67.  *      WM_SETFONT message.  You can get the current font at any time with
  68.  *      the WM_GETFONT message.
  69.  *
  70.  *      For other messages and the documentation, see the header block
  71.  *      for midiKeyBProc at the end of this source file.
  72.  *
  73.  *
  74.  **/
  75.  
  76.  
  77. /* get the includes we need */
  78.     #include <windows.h>
  79.     #include "midikeyb.h"
  80.  
  81.  
  82. /* static global variables */
  83.     static char gszmidiKeyBClass[] = "midiKeyB";
  84.  
  85.     typedef HANDLE      HMIDIKEYB; 
  86.  
  87.  
  88. /* I wonder why this isn't defined? */
  89.     #define MAKEWORD(bLow, bHigh)   ((WORD)((bHigh<<8)|bLow))
  90.  
  91.  
  92. /* for debuggin' */
  93.     #ifdef DEBUG
  94.         #define D(x)    {x;}
  95.         #define ODS(x)  OutputDebugString(x)
  96.     #else
  97.         #define D(x)
  98.         #define ODS(x)
  99.     #endif
  100.  
  101.  
  102. /* window word position definitions */
  103.     #define KEYB_WW_HMIDIKEYB   0
  104.     #define KEYB_WW_EXTRABYTES  2
  105.  
  106.     #define KEYB_GMEM_FLAGS     (GMEM_MOVEABLE | GMEM_ZEROINIT)
  107.  
  108.  
  109. /* defaults for midiKeyB controls */
  110.     #define KEYB_DEF_NUMKEYS    36  /* total number of keys             */
  111.     #define KEYB_DEF_FIRSTKEY   48  /* 60 is middle C (inc semi-tones)  */
  112.     #define KEYB_DEF_VELOCITY   48  /* default note velocity            */
  113.     #define KEYB_DEF_BKCOLOR    RGB( 255, 255, 255 )
  114.     #define KEYB_DEF_FGCOLOR    RGB( 0, 0, 0 )
  115.  
  116.     #define KEYB_MAX_KEYS       128 /* maximum number of MIDI notes     */
  117.  
  118.  
  119. /* typedefs for the keyboard */
  120.     typedef struct tKEYBKEY
  121.     {
  122.         RECT        rc;             /* the key's area                   */
  123.         BYTE        bMIDINote;      /* which midi key number            */
  124.         BYTE        bNote;          /* which (alphabetical) note        */
  125.         BOOL        fNoteOn;        /* is the note on?                  */
  126.  
  127.     } KEYBKEY, *PKEYBKEY, FAR *LPKEYBKEY;
  128.  
  129.     typedef struct tMIDIKEYB
  130.     {
  131.         BYTE        bNumKeys;       /* number of keys on the keyboard   */
  132.         BYTE        bFirstKey;      /* first key on the keyboard        */
  133.         BYTE        bWhiteKeys;     /* number of white keys             */
  134.         BYTE        bLastNote;      /* last note played                 */
  135.         BYTE        bChannel;       /* channel keyboard is assigned to  */
  136.         BYTE        bVelocity;      /* velocity for keys                */
  137.         WORD        wTMHeight;      /* height from textmetric struct    */
  138.         WORD        wTMAvgWidth;    /* width from textmetric struct     */
  139.         WORD        wKeyAdd;        /* fudge factor                     */
  140.         DWORD       rgbFgColor;     /* foreground color                 */
  141.         DWORD       rgbBkColor;     /* background color                 */
  142.         HANDLE      hMidiOut;       /* midi output handle for msg       */
  143.         HWND        hWnd;           /* optional window handle to post   */
  144.         WORD        wWidth;         /* client window width              */
  145.         WORD        wHeight;        /* client window height             */
  146.         WORD        wMsgDown;       /* message to send on key down event*/
  147.         WORD        wMsgUp;         /* message to send on key up event  */
  148.         HFONT       hFont;          /* font to use for labels           */
  149.         KEYBKEY     rgKeyBKeys[ KEYB_MAX_KEYS ];
  150.  
  151.     } MIDIKEYB, *PMIDIKEYB, FAR *LPMIDIKEYB;
  152.  
  153.  
  154. /* internal function prototypes */
  155.     LONG FAR PASCAL midiKeyBProc( HWND, WORD, WORD, LONG );
  156.     void NEAR PASCAL midiKeyBPaint( HWND hWnd, HDC hDC );
  157.     BYTE NEAR PASCAL midiKeyBGetNote( LPMIDIKEYB lpKeyB, LONG pos );
  158.     BYTE NEAR PASCAL midiKeyBNormalKey( BYTE bKey );
  159.     void NEAR PASCAL midiKeyBNoteChanged( HWND hWnd, BYTE bNote );
  160.     void NEAR PASCAL midiKeyBNoteOn( HWND hWnd, BYTE bNote );
  161.     void NEAR PASCAL midiKeyBNoteOff( HWND hWnd, BYTE bNote );
  162.  
  163.  
  164.  
  165. /** BOOL FAR PASCAL midiKeyBInit( HANDLE hInst )
  166.  *
  167.  *  DESCRIPTION: 
  168.  *      Registers the window class for the midiKeyB control.  This must
  169.  *      be done before the midiKeyB control is used--or it will fail
  170.  *      and your dialog box will not open!
  171.  *
  172.  *  ARGUMENTS:
  173.  *      HANDLE hInst    :   Instance handle to register class with.
  174.  *
  175.  *  RETURN (BOOL):
  176.  *      The return value is TRUE if the midiKeyB class was successfully
  177.  *      registered.  It is FALSE if the initialization failed.
  178.  *
  179.  *  NOTES:
  180.  *
  181.  **/
  182.  
  183. #ifndef DEBUG
  184.     /* codeview pukes if you have this in--why? */
  185.     #pragma alloc_text( init, midiKeyBInit )
  186. #endif
  187.  
  188. BOOL FAR PASCAL midiKeyBInit( HANDLE hInst )
  189. {
  190.     static BOOL fRegistered = FALSE;
  191.     WNDCLASS    rClass;
  192.     
  193.     /* assume already registered if not first instance */
  194.     if ( fRegistered )
  195.         return ( TRUE );
  196.  
  197.     /* fill in the class structure for the midiKeyB control */
  198.     if ( !(rClass.hCursor = LoadCursor( hInst, "CURSOR_MIDIKEYB" )) )
  199.         rClass.hCursor = LoadCursor( NULL, IDC_UPARROW );
  200.  
  201.     rClass.hIcon        = NULL;
  202.     rClass.lpszMenuName = NULL;
  203.     rClass.lpszClassName= gszmidiKeyBClass;
  204.     rClass.hbrBackground= GetStockObject( WHITE_BRUSH );
  205.     rClass.hInstance    = hInst;
  206.  
  207. #ifdef MIDIKEYB_DLL
  208.     rClass.style        = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
  209. #else
  210.     rClass.style        = CS_HREDRAW | CS_VREDRAW;
  211. #endif
  212.  
  213.     rClass.lpfnWndProc  = midiKeyBProc;
  214.     rClass.cbClsExtra   = 0;
  215.     rClass.cbWndExtra   = KEYB_WW_EXTRABYTES;
  216.  
  217.     /* attempt to register it--create abs Boolean in fRegistered */
  218.     return ( fRegistered = (RegisterClass( &rClass ) != NULL) );
  219. } /* midiKeyBInit() */
  220.  
  221.  
  222. /** BYTE NEAR PASCAL midiKeyBNormalKey( BYTE bKey )
  223.  *
  224.  *  DESCRIPTION: 
  225.  *      This function returns the normal (white) key name (alphabetic).
  226.  *      All accidental (black) keys return 0.
  227.  *
  228.  *  ARGUMENTS:
  229.  *      BYTE bKey           :   The key in question.
  230.  *
  231.  *  RETURN (BYTE):
  232.  *      If the key is a normal key (white), then its alphabetic name is 
  233.  *      returned (character).  If the key is an accidental key, then 0 is
  234.  *      returned.
  235.  *
  236.  *  NOTES:
  237.  *
  238.  **/
  239.  
  240. BYTE NEAR PASCAL midiKeyBNormalKey( BYTE bKey )
  241. {
  242.     /* assumes C0 is 0, ..., C4 is 60 (middle C)... */
  243.     switch ( bKey % 12 )
  244.     {
  245.         case 0:
  246.             return ( 'C' );
  247.         case 2:
  248.             return ( 'D' );
  249.         case 4:
  250.             return ( 'E' );
  251.         case 5:
  252.             return ( 'F' );
  253.         case 7:
  254.             return ( 'G' );
  255.         case 9:
  256.             return ( 'A' );
  257.         case 11:
  258.             return ( 'B' );
  259.     }
  260.  
  261.     /* not a normal key--must be accidental */
  262.     return ( 0 );
  263. } /* midiKeyBNormalKey() */
  264.  
  265.  
  266. /** void NEAR PASCAL midiKeyBNoteOn( HWND hWnd, BYTE bNote )
  267.  *
  268.  *  DESCRIPTION: 
  269.  *      This function is used to turn on a MIDI note.  This includes sending
  270.  *      a message to the parent window and setting the state of the key to
  271.  *      ON.
  272.  *
  273.  *  ARGUMENTS:
  274.  *      HWND hWnd   :   Handle to control's window.
  275.  *
  276.  *      BYTE bNote  :   The note to send the 'ON' or 'DOWN' message for.
  277.  *
  278.  *  RETURN (void):
  279.  *      The note will have been turned on.
  280.  *
  281.  *  NOTES:
  282.  *
  283.  **/
  284.  
  285. void NEAR PASCAL midiKeyBNoteOn( HWND hWnd, BYTE bNote )
  286. {
  287.     HMIDIKEYB    hKeyB;
  288.     LPMIDIKEYB   lpKeyB;
  289.  
  290.     /* get the handle to the keyboard's control block */
  291.     hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  292.  
  293.     /* get pointer to the control's control block */
  294.     if ( !(lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB )) )
  295.         return;
  296.  
  297.     /* set the current key state */
  298.     lpKeyB->bLastNote = bNote;
  299.  
  300.     /* don't send note on message if the key is already down */
  301.     if ( !lpKeyB->rgKeyBKeys[ bNote ].fNoteOn )
  302.     {
  303.         lpKeyB->rgKeyBKeys[ bNote ].fNoteOn = TRUE;
  304.  
  305.         /* determine window to send message to */
  306.         hWnd = (lpKeyB->hWnd) ? lpKeyB->hWnd : GetParent( hWnd );
  307.  
  308.         SendMessage( hWnd, lpKeyB->wMsgDown, lpKeyB->hMidiOut,
  309.                     MAKELONG( MAKEWORD( (BYTE)0x90 + lpKeyB->bChannel, bNote ),
  310.                             MAKEWORD( lpKeyB->bVelocity, 0 ) ) );
  311.     }
  312.  
  313.     /* unlock it */
  314.     GlobalUnlock( hKeyB );
  315. } /* midiKeyBNoteOn() */
  316.  
  317.  
  318. /** void NEAR PASCAL midiKeyBNoteOff( HWND hWnd, BYTE bNote )
  319.  *
  320.  *  DESCRIPTION: 
  321.  *      This function is used to turn off a MIDI note.  This includes sending
  322.  *      a message to the parent window and setting the state of the key to
  323.  *      OFF.
  324.  *
  325.  *  ARGUMENTS:
  326.  *      HWND hWnd   :   Handle to control's window.
  327.  *
  328.  *      BYTE bNote  :   The note to send the 'OFF' or 'UP' message for.
  329.  *
  330.  *  RETURN (void):
  331.  *      The note will have been turned off.
  332.  *
  333.  *  NOTES:
  334.  *
  335.  **/
  336.  
  337. void NEAR PASCAL midiKeyBNoteOff( HWND hWnd, BYTE bNote )
  338. {
  339.     HMIDIKEYB    hKeyB;
  340.     LPMIDIKEYB   lpKeyB;
  341.  
  342.     /* get the handle to the keyboard's control block */
  343.     hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  344.  
  345.     /* get pointer to the control's control block */
  346.     if ( !(lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB )) )
  347.         return;
  348.  
  349.     /* don't send note off message if the key is already up */
  350.     if ( lpKeyB->rgKeyBKeys[ bNote ].fNoteOn )
  351.     {
  352.         /* set the current key state */
  353.         lpKeyB->rgKeyBKeys[ bNote ].fNoteOn = FALSE;
  354.  
  355.         /* determine window to send message to */
  356.         hWnd = (lpKeyB->hWnd) ? lpKeyB->hWnd : GetParent( hWnd );
  357.  
  358.         SendMessage( hWnd, lpKeyB->wMsgUp, lpKeyB->hMidiOut,
  359.                     MAKELONG( MAKEWORD( (BYTE)0x80 + lpKeyB->bChannel, bNote ),
  360.                             MAKEWORD( 0, 0 ) ) );
  361.     }
  362.  
  363.     /* unlock it */
  364.     GlobalUnlock( hKeyB );
  365. } /* midiKeyBNoteOff() */
  366.  
  367.  
  368. /** BYTE NEAR PASCAL midiKeyBGetNote( LPMIDIKEYB lpKeyB, LONG lPos )
  369.  *
  370.  *  DESCRIPTION: 
  371.  *      This function does the 'hit-testing' for the keyboard.  Although
  372.  *      there is probably a better solution to approximate the key, this
  373.  *      works well enough for now.
  374.  *
  375.  *  ARGUMENTS:
  376.  *      LPMIDIKEYB lpKeyB   :   Pointer to keyboard layout information.
  377.  *
  378.  *      LONG lPos           :   Point to hit-test against (lParam of mouse
  379.  *                              message).
  380.  *
  381.  *  RETURN (BYTE):
  382.  *      The return value is the MIDI note number of the key hit.  It is
  383.  *      0xFF if no key was 'hit.'
  384.  *
  385.  *  NOTES:
  386.  *
  387.  **/
  388.  
  389.     #define KEYB_GUESS_ERROR    6       /* no >5 w/ current algorithm   */
  390.  
  391. BYTE NEAR PASCAL midiKeyBGetNote( LPMIDIKEYB lpKeyB, LONG lPos )
  392. {
  393.     POINT   pt;
  394.     BYTE    i;
  395.     WORD    dx;
  396.     WORD    wKey;
  397.  
  398.     pt.x = LOWORD( lPos );
  399.     pt.y = HIWORD( lPos );
  400.  
  401.     /* dx is the width of a white key */
  402.     dx = lpKeyB->wWidth / lpKeyB->bWhiteKeys;
  403.  
  404.     /*  Now _approximate_ the key hit (guess *LOW*).  This is probably not
  405.      *  the most efficient approximation--but it works.  And it's a whole
  406.      *  bunch better than doing a bajillion PtInRect() calls!  Currently,
  407.      *  the error will be no >5 keys off.
  408.      *
  409.      *  If you are trying to figure out what this calculation is doing, it
  410.      *  might help if I said that there are 12 keys per octave; 7 white and
  411.      *  5 black.  So it uses this ratio to zero in on our key.  If you can
  412.      *  simplify this, tell me how.
  413.      */
  414.     wKey = ((lpKeyB->bFirstKey / 12) * 7) +
  415.                 (((lpKeyB->bFirstKey % 12) * 5) / 7) +
  416.                 (pt.x / dx);
  417.     wKey += (wKey / 7) * 5;
  418.  
  419.     /* for computing error when debugging (overload dx...) */
  420.     D( dx = 0 );
  421.  
  422.     /* step from the guess, up... */
  423.     for ( i = (BYTE)wKey; (i < lpKeyB->bFirstKey + lpKeyB->bNumKeys) &&
  424.                           (i < (BYTE)(wKey + KEYB_GUESS_ERROR)); i++ )
  425.     {
  426.         /* black notes are 'over' white ones */
  427.         if ( !lpKeyB->rgKeyBKeys[i].bNote )
  428.         {
  429.             if ( PtInRect( &(lpKeyB->rgKeyBKeys[i].rc), pt ) )
  430.             {
  431. #if 0
  432.                 D(  char    tBuf[ 80 ];
  433.                     wsprintf( tBuf, "Black Error: %d\r\n", dx );
  434.                     ODS( tBuf );
  435.                  );
  436. #endif
  437.  
  438.                 return ( (BYTE)(i + lpKeyB->wKeyAdd) );
  439.             }
  440.         }
  441.  
  442.         /* for computing error when debugging */
  443.         D( dx++ );
  444.     }
  445.     
  446.     /* for computing error when debugging */
  447.     D( dx = 0 );
  448.  
  449.     for ( i = (BYTE)wKey; (i < lpKeyB->bFirstKey + lpKeyB->bNumKeys) &&
  450.                           (i < (BYTE)(wKey + KEYB_GUESS_ERROR)); i++ )
  451.     {
  452.         /* look at white keys */
  453.         if ( lpKeyB->rgKeyBKeys[i].bNote ) 
  454.         {
  455.             if ( PtInRect( &(lpKeyB->rgKeyBKeys[i].rc), pt ) )
  456.             {
  457. #if 0
  458.                 D(  char    tBuf[ 80 ];
  459.                     wsprintf( tBuf, "White Error: %d\r\n", dx );
  460.                     ODS( tBuf );
  461.                  );
  462. #endif
  463.  
  464.                 return ( (BYTE)(i + lpKeyB->wKeyAdd) );
  465.             }
  466.         }
  467.  
  468.         /* for computing error when debugging */
  469.         D( dx++ );
  470.     }
  471.  
  472.     /* out of bounds */
  473.     return ( 0xFF );
  474. } /* midiKeyBGetNote() */
  475.  
  476.  
  477. /** void midiKeyBPaintKey( hWnd, hDC, lpKeyB, hPenLine, i )
  478.  *
  479.  *  DESCRIPTION: 
  480.  *      This chunk of junk paints a single key.  It is used by both paint
  481.  *      routines.  There is no sense duplicating code everywhere.
  482.  *
  483.  *  ARGUMENTS:
  484.  *      HWND hWnd           :   The window to munge in.
  485.  *
  486.  *      HDC hDC             :   The DC to munge.
  487.  *
  488.  *      LPMIDIKEYB lpKeyB   :   Pointer to keyboard info.
  489.  *
  490.  *      HPEN hPenLine       :   Pen for drawing key separators.
  491.  *
  492.  *      BYTE i              :   The MIDI key note number to paint.
  493.  *
  494.  *  RETURN (void):
  495.  *      The key will have been painted.
  496.  *
  497.  *  NOTES:
  498.  *
  499.  **/
  500.  
  501. void midiKeyBPaintKey( HWND hWnd, HDC hDC, LPMIDIKEYB lpKeyB, HPEN hPenLine, BYTE i )
  502. {
  503.     LPKEYBKEY   lpKeyBKey = &lpKeyB->rgKeyBKeys[ i ];
  504.     BOOL        hOldFont = NULL;
  505.     DWORD       dwOldText, dwOldBk;
  506.     char        buf[ 4 ];
  507.     char        buf2[ 4 ];
  508.     RECT        rc, rc2;
  509.     int         nSeparatorTop;
  510.     int         nLen;
  511.  
  512.     /* don't munge original (CopyRect is silly and slow!) */
  513.     rc2 = rc = lpKeyBKey->rc;
  514.     rc2.top = rc.top = rc.bottom - (2 * lpKeyB->wTMHeight);
  515.  
  516.     /* paint white keys */
  517.     if ( lpKeyBKey->bNote )
  518.     {
  519.         /* set the colors into for the keyboard into the control */
  520.         dwOldText = SetTextColor( hDC, lpKeyB->rgbFgColor );
  521.         dwOldBk = SetBkColor( hDC, lpKeyB->rgbBkColor );
  522.  
  523.         /* for labels */
  524.         if ( GetWindowLong(hWnd, GWL_STYLE) & KEYBS_LABELS )
  525.         {
  526.             if ( lpKeyB->hFont )
  527.                 hOldFont = SelectObject( hDC, lpKeyB->hFont );
  528.  
  529.             nLen = wsprintf( buf, "%u", i + lpKeyB->wKeyAdd );
  530.             wsprintf( buf2, "%c", lpKeyBKey->bNote );
  531.         }
  532.  
  533.         /* zero len strings if no labels */
  534.         else *buf2 = *buf = '\0', nLen = 0;
  535.  
  536.         InflateRect( &rc2, -1, -1 );
  537.  
  538.         /* set top of ON rect to not overlap black keys */
  539.         rc2.top = max( rc2.top, (int)(lpKeyB->wHeight * 2/3) );
  540.  
  541.         ExtTextOut( hDC, lpKeyBKey->rc.left + (lpKeyBKey->rc.right -
  542.                     lpKeyBKey->rc.left - lpKeyB->wTMAvgWidth * nLen) / 2,
  543.                     lpKeyB->wHeight - 2 * lpKeyB->wTMHeight, 
  544.                     ETO_CLIPPED | ETO_OPAQUE, &rc2,
  545.                     (LPSTR)buf, nLen, NULL );
  546.  
  547.         ExtTextOut( hDC, lpKeyBKey->rc.right - (lpKeyBKey->rc.right -
  548.                     lpKeyBKey->rc.left) / 2 - lpKeyB->wTMAvgWidth / 2,
  549.                     lpKeyB->wHeight - lpKeyB->wTMHeight, NULL, NULL,
  550.                     (LPSTR)buf2, lstrlen( buf2 ), NULL );
  551.  
  552.         /* set top of ON rect to not overlap black keys */
  553.         rc.top = max( rc.top, (int)(lpKeyB->wHeight * 2/3) );
  554.  
  555.         /* is the next key an accidental key? adjust separator if so */
  556.         if ( (i < 127) && lpKeyB->rgKeyBKeys[ i + 1 ].bNote )
  557.             nSeparatorTop = lpKeyBKey->rc.top;
  558.         else
  559.             nSeparatorTop = lpKeyB->rgKeyBKeys[ i + 1 ].rc.bottom;
  560.  
  561.         hPenLine = SelectObject( hDC, hPenLine );
  562.         MoveTo( hDC, lpKeyBKey->rc.right, nSeparatorTop );
  563.         LineTo( hDC, lpKeyBKey->rc.right, lpKeyBKey->rc.bottom );
  564.         hPenLine = SelectObject( hDC, hPenLine );
  565.  
  566.         if ( hOldFont )
  567.             SelectObject( hDC, hOldFont );
  568.     }
  569.  
  570.     /* paint black keys */
  571.     else
  572.     {
  573.         /* set the colors into for the keyboard into the control */
  574.         dwOldBk = SetBkColor( hDC, lpKeyB->rgbFgColor );
  575.         dwOldText = SetTextColor( hDC, lpKeyB->rgbBkColor );
  576.  
  577.         ExtTextOut( hDC, lpKeyBKey->rc.left, lpKeyBKey->rc.top,
  578.                     ETO_CLIPPED | ETO_OPAQUE, &lpKeyBKey->rc, NULL,
  579.                     0, NULL );
  580.  
  581.     }
  582.  
  583.     /* put these back */
  584.     SetTextColor( hDC, dwOldText );
  585.     SetBkColor( hDC, dwOldBk );
  586.  
  587.     if ( lpKeyB->rgKeyBKeys[ i + lpKeyB->wKeyAdd ].fNoteOn )
  588.     {
  589.         InflateRect( &rc, -2, -2 );
  590.         InvertRect( hDC, &rc );
  591.     }
  592. } /* midiKeyBPaintKey() */
  593.  
  594.  
  595. /** void NEAR PASCAL midiKeyBPaintSingleKey( HWND hWnd, BYTE bNote )
  596.  *
  597.  *  DESCRIPTION: 
  598.  *      This function is used to paint a *single* key when it changes state.
  599.  *      Even though GDI will only paint what has been InvalidateRect()'d,
  600.  *      it must do clipping which is slow when dealing with a BIG keyboard.
  601.  *      This is about 100 times faster than defaulting through WM_PAINT.
  602.  *
  603.  *  ARGUMENTS:
  604.  *      HWND hWnd   :   Window handle for the custom control.
  605.  *
  606.  *      BYTE bNote  :   The note (MIDI note number) that changed state.
  607.  *
  608.  *  RETURN (void):
  609.  *      The key will have been painted.
  610.  *
  611.  *  NOTES:
  612.  *
  613.  **/
  614.  
  615. void NEAR PASCAL midiKeyBPaintSingleKey( HWND hWnd, BYTE bNote )
  616. {
  617.     HMIDIKEYB   hKeyB;
  618.     LPMIDIKEYB  lpKeyB;
  619.     HPEN        hPenLine;
  620.     HDC         hDC;
  621.  
  622.     /* get the handle to the keyboard's control block */
  623.     hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  624.  
  625.     /* get pointer to the control's control block */
  626.     if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  627.     {
  628.         /* get the hDC and prepare for painting the key */
  629.         if ( hDC = GetDC( hWnd ) )
  630.         {
  631.             /* build pen here so it isn't built for *each* key we paint */
  632.             if ( hPenLine = CreatePen( PS_SOLID, 1, lpKeyB->rgbFgColor ) )
  633.             {
  634.                 /* paint it */
  635.                 midiKeyBPaintKey( hWnd, hDC, lpKeyB, hPenLine, bNote );
  636.  
  637.                 /* get rid of the pen */
  638.                 DeleteObject( hPenLine );
  639.             }
  640.  
  641.             ReleaseDC( hWnd, hDC );
  642.         }
  643.  
  644.         /* now unlock the control block */
  645.         GlobalUnlock( hKeyB );
  646.     }
  647. } /* midiKeyBPaintSingleKey() */
  648.  
  649.  
  650. /** void NEAR PASCAL midiKeyBNoteChanged( HWND hWnd, BYTE bNote )
  651.  *
  652.  *  DESCRIPTION: 
  653.  *      This function is called to change the state of a key.  The key
  654.  *      will be repainted in accordance to the new state.
  655.  *
  656.  *  ARGUMENTS:
  657.  *      HWND hWnd           :   Window handle of control.
  658.  *
  659.  *      LPMIDIKEYB lpKeyB   :   Pointer to keyboard layout information.
  660.  *
  661.  *      BYTE bNote          :   MIDI note number to toggle.
  662.  *
  663.  *  RETURN (void):
  664.  *      The MIDI note 'bNote' will have been toggled.
  665.  *
  666.  *  NOTES:
  667.  *
  668.  **/
  669.  
  670. void NEAR PASCAL midiKeyBNoteChanged( HWND hWnd, BYTE bNote )
  671. {
  672.     HMIDIKEYB   hKeyB;
  673.     LPMIDIKEYB  lpKeyB;
  674.     RECT        rc;
  675.  
  676.     /* get the handle to the keyboard's control block */
  677.     hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  678.  
  679.     /* get pointer to the control's control block */
  680.     if ( !(lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB )) )
  681.         return;
  682.  
  683.     /* don't munge original copy (CopyRect is silly and slow!) */
  684.     rc = lpKeyB->rgKeyBKeys[ bNote ].rc;
  685.     rc.top = rc.bottom - lpKeyB->wTMHeight * 2;
  686.  
  687.     /* set top of ON rect to not overlap black keys */
  688.     if ( midiKeyBNormalKey( bNote ) )
  689.         rc.top = max( rc.top, (int)(lpKeyB->wHeight * 2 / 3) );
  690.  
  691.     /* a little bit _inside_ of the key */
  692.     InflateRect( &rc, -2, -2 );
  693.  
  694.     /* paint a single key */
  695.     midiKeyBPaintSingleKey( hWnd, bNote );
  696.  
  697.     /* unlock it */
  698.     GlobalUnlock( hKeyB );
  699. } /* midiKeyBNoteChanged() */
  700.  
  701.  
  702. /** void NEAR PASCAL midiKeyBPaint( HWND hWnd, HDC hDC )
  703.  *
  704.  *  DESCRIPTION: 
  705.  *      This function is responsible for painting the midiKeyB control.
  706.  *
  707.  *  ARGUMENTS:
  708.  *      HWND hWnd   :   The window handle for the keyboard.
  709.  *
  710.  *      HDC hDC     :   The DC for the keyboard's window.
  711.  *
  712.  *  RETURN (void):
  713.  *      The control will have been painted.
  714.  *
  715.  *  NOTES:
  716.  *
  717.  **/
  718.  
  719. void NEAR PASCAL midiKeyBPaint( HWND hWnd, HDC hDC )
  720. {
  721.     HMIDIKEYB   hKeyB;
  722.     LPMIDIKEYB  lpKeyB;
  723.     WORD        i;
  724.     WORD        wLastKey;
  725.     HPEN        hPenLine;
  726.  
  727.     /* get the handle to the keyboard's control block */
  728.     hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  729.  
  730.     /* get pointer to the control's control block */
  731.     if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  732.     {
  733.         /* build pen here so it isn't built for *each* key we paint */
  734.         if ( hPenLine = CreatePen( PS_SOLID, 1, lpKeyB->rgbFgColor ) )
  735.         {
  736.             /* precompute the last MIDI key to paint */
  737.             wLastKey = lpKeyB->bFirstKey + lpKeyB->bNumKeys;
  738.  
  739.             for ( i = lpKeyB->bFirstKey; i < wLastKey; i++ )
  740.             {
  741.                 /* paint it */
  742.                 midiKeyBPaintKey( hWnd, hDC, lpKeyB, hPenLine, (BYTE)i );
  743.             }
  744.  
  745.             /* get rid of the pen */
  746.             DeleteObject( hPenLine );
  747.         }
  748.  
  749.         /* now unlock the control block */
  750.         GlobalUnlock( hKeyB );
  751.     }
  752. } /* midiKeyBPaint() */
  753.  
  754.   
  755. /** void midiKeyBSize( HWND hWnd, LPMIDIKEYB lpKeyB )
  756.  *
  757.  *  DESCRIPTION: 
  758.  *      This function is used to recalculate the rectangles used for painting
  759.  *      and hit testing the keys on the keyboard.
  760.  *
  761.  *  ARGUMENTS:
  762.  *      HWND hWnd           :   The window to size the control to.
  763.  *
  764.  *      LPMIDIKEYB lpKeyB   :   Keyboard layout information.
  765.  *
  766.  *  RETURN (void):
  767.  *      The rectangles for each key will have been recalculated.
  768.  *
  769.  *  NOTES:
  770.  *
  771.  **/
  772.  
  773. void midiKeyBSize( HWND hWnd, LPMIDIKEYB lpKeyB )
  774. {
  775.     WORD    i, x, dx;
  776.  
  777.     /* count the number of white keys visible */
  778.     lpKeyB->bWhiteKeys = 0;
  779.     for ( i = lpKeyB->bFirstKey; i < lpKeyB->bFirstKey + lpKeyB->bNumKeys; i++ )
  780.     {
  781.         if ( midiKeyBNormalKey( (BYTE)i ) )
  782.             lpKeyB->bWhiteKeys++;
  783.     }
  784.  
  785.     /* x is the right side of the white, dx is the width of a white key */
  786.     x = dx = lpKeyB->wWidth / lpKeyB->bWhiteKeys;
  787.  
  788.     for ( i = lpKeyB->bFirstKey; i < lpKeyB->bFirstKey + lpKeyB->bNumKeys; i++ )
  789.     {
  790.         if ( lpKeyB->rgKeyBKeys[i].bNote )
  791.         {
  792.             /* it's a normal (white) key */
  793.             SetRect( &(lpKeyB->rgKeyBKeys[i].rc), x - dx, 0, x, lpKeyB->wHeight );
  794.             x += dx;
  795.         }
  796.  
  797.         else
  798.         {                            
  799.             /* it's an accidental (black) key */
  800.             SetRect( &(lpKeyB->rgKeyBKeys[i].rc), x-dx/3-dx, 0, x+dx/3-dx, lpKeyB->wHeight*2/3 );
  801.         }
  802.     }
  803. } /* midiKeyBSize() */
  804.  
  805.  
  806. /** BOOL NEAR PASCAL midiKeyBShortMsg( HWND hWnd, LPMIDIKEYB lpKeyB, DWORD dwMsg )
  807.  *
  808.  *  DESCRIPTION:
  809.  *      This function is responsible for parsing and performing the MIDI
  810.  *      short message passed in dwMsg.  At this time, NOTE ON and NOTE OFF
  811.  *      are the only messages that we respond to--this would be easy to
  812.  *      expand upon.
  813.  *
  814.  *  ARGUMENTS:
  815.  *      HWND hWnd           :   Window handle of midiKeyB control.
  816.  *
  817.  *      LPMIDIKEYB lpKeyB   :   Pointer to midiKeyB instance data.
  818.  *
  819.  *      DWORD dwMsg         :   The MIDI short message to interpret.
  820.  *
  821.  *  RETURN (BOOL):
  822.  *      Currently always returns TRUE.
  823.  *
  824.  *  NOTES:
  825.  *
  826.  **/
  827.  
  828. BOOL NEAR PASCAL midiKeyBShortMsg( HWND hWnd, LPMIDIKEYB lpKeyB, DWORD dwMsg )
  829. {
  830.     BYTE    bCmd    = (BYTE)(((BYTE)dwMsg) & 0xF0);
  831.     BYTE    bNote   = (BYTE)(((WORD)dwMsg) >> 8);
  832.  
  833.     switch ( bCmd )
  834.     {
  835.         /* key up */
  836.         case 0x80:
  837.             /* don't do a bunch of silly stuff if it is already up */
  838.             if ( lpKeyB->rgKeyBKeys[ bNote ].fNoteOn )
  839.             {
  840.                 /* set the current key state */
  841.                 lpKeyB->rgKeyBKeys[ bNote ].fNoteOn = FALSE;
  842.  
  843.                 /* show it */
  844.                 midiKeyBNoteChanged( hWnd, bNote );
  845.             }
  846.         break;
  847.  
  848.         /* key down */
  849.         case 0x90:
  850.             /* don't do a bunch of silly stuff if it is already down */
  851.             if ( !lpKeyB->rgKeyBKeys[ bNote ].fNoteOn )
  852.             {
  853.                 /* set the current key state */
  854.                 lpKeyB->rgKeyBKeys[ bNote ].fNoteOn = TRUE;
  855.  
  856.                 /* show it */
  857.                 midiKeyBNoteChanged( hWnd, bNote );
  858.             }
  859.         break;
  860.     }
  861.  
  862.     /* always succeed for now... */
  863.     return ( TRUE );
  864. } /* midiKeyBShortMsg() */
  865.  
  866.  
  867. /** LONG FAR PASCAL midiKeyBProc( HWND hWnd, WORD wMsg, WORD wParam, LONG lParam )
  868.  *
  869.  *  DESCRIPTION: 
  870.  *      This is the control's window procedure.  Its purpose is to handle
  871.  *      special messages for this custom control.
  872.  *
  873.  *      The special control messages for the midiKeyB control are:
  874.  *
  875.  *          KEYB_GETLAYOUT  :   Gets the current keyboard layout.  HIWORD()
  876.  *                              is the first key; LOWORD() is number of keys.
  877.  *
  878.  *          KEYB_SETLAYOUT  :   Sets the keyboard layout.  HIWORD(lParam) is
  879.  *                              the first key; LOWORD(lParam) is the number
  880.  *                              of keys for the keyboard.  wParam is a BOOL
  881.  *                              telling the control to repaint now (TRUE) or
  882.  *                              later (FALSE).
  883.  *
  884.  *          KEYB_GETCHANNEL :   Gets the current channel used in the MIDI
  885.  *                              short message (in lParam) sent to the parent
  886.  *                              window.
  887.  *
  888.  *          KEYB_SETCHANNEL :   Sets the current channel used in the MIDI
  889.  *                              short message (in lParam) sent to the parent
  890.  *                              window.  wParam should be the new channel.
  891.  *                              lParam is ignored.
  892.  *
  893.  *          KEYB_GETHMIDIOUT:   Gets the current hMidiOut used in the MIDI
  894.  *                              short message (in lParam) sent to the parent
  895.  *                              window.
  896.  *
  897.  *          KEYB_SETHMIDIOUT:   Sets the current hMidiOut used in the MIDI
  898.  *                              short message (in lParam) sent to the parent
  899.  *                              window.  wParam should be the new hMidiOut.
  900.  *                              lParam is ignored.
  901.  *
  902.  *          KEYB_GETHWND    :   Gets the current 'parent' window that will
  903.  *                              receive note on and off messages from the
  904.  *                              control.
  905.  *
  906.  *          KEYB_SETHWND    :   Sets the current 'parent' window that will
  907.  *                              receive note on and off messages.  If NULL,
  908.  *                              then the GetParent() of the control will
  909.  *                              receive the messages (default).
  910.  *
  911.  *          KEYB_GETVELOCITY:   Gets the current velocity used in the MIDI
  912.  *                              short message (in lParam) sent to the parent
  913.  *                              window.
  914.  *
  915.  *          KEYB_SETVELOCITY:   Sets the current velocity used in the MIDI
  916.  *                              short message (in lParam) sent to the parent
  917.  *                              window.  wParam should be the new velocity.
  918.  *                              lParam is ignored.
  919.  *
  920.  *          KEYB_GETFGCOLOR :   Gets the current foreground color of keyboard.
  921.  *                              This is used for the accidental keys, text,
  922.  *                              and separator lines.
  923.  *
  924.  *          KEYB_SETFGCOLOR :   Sets the foreground color for the keyboard.
  925.  *                              This is used for the accidental keys, text,
  926.  *                              and separator lines.  lParam should be the
  927.  *                              RGB() value.  wParam is a BOOL telling the
  928.  *                              control to repaint immediately (TRUE) or
  929.  *                              later (FALSE).
  930.  *
  931.  *          KEYB_GETBKCOLOR :   Gets the current background color of keyboard.
  932.  *                              This is used for the normal keys.
  933.  *
  934.  *          KEYB_SETBKCOLOR :   Sets the background color for the keyboard.
  935.  *                              This is used for the normal keys. lParam
  936.  *                              should be the RGB() value.  wParam is a BOOL
  937.  *                              telling the control to repaint immediately
  938.  *                              (TRUE) or later (FALSE).
  939.  *
  940.  *          WM_SETFONT      :   Sets the font to use for the labels on keys.
  941.  *
  942.  *          WM_GETFONT      :   Gets the current font in use for labels.
  943.  *
  944.  *  NOTES:
  945.  *
  946.  **/
  947.  
  948. LONG FAR PASCAL midiKeyBProc( HWND hWnd, WORD wMsg, WORD wParam, LONG lParam )
  949. {
  950.     PAINTSTRUCT ps;
  951.     HMIDIKEYB   hKeyB;
  952.     LPMIDIKEYB  lpKeyB;
  953.     WORD        i;
  954.     BYTE        bNote;
  955.     HFONT       hFont;
  956.  
  957.     /* break to get DefWindowProc() */
  958.     switch( wMsg )
  959.     {
  960.         case WM_CREATE:
  961.             /* need to allocate a control block */
  962.             if ( hKeyB = GlobalAlloc(KEYB_GMEM_FLAGS, sizeof(MIDIKEYB)) )
  963.             {
  964.                 /* hang on to this control block */
  965.                 SetWindowWord( hWnd, KEYB_WW_HMIDIKEYB, hKeyB );
  966.  
  967.                 /* lock the control block so we can fill it with defaults */
  968.                 if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  969.                 {
  970.                     HDC         hDC;
  971.                     TEXTMETRIC  tm;
  972.  
  973.                     /* grab the text metrics */
  974.                     hDC = GetDC( hWnd );
  975.                     GetTextMetrics( hDC, &tm );
  976.  
  977.                     /* fill control block with defaults */
  978.                     lpKeyB->wTMHeight   = tm.tmHeight;
  979.                     lpKeyB->wTMAvgWidth = tm.tmAveCharWidth;
  980.                     lpKeyB->bVelocity   = KEYB_DEF_VELOCITY;
  981.                     lpKeyB->rgbFgColor  = GetNearestColor( hDC, KEYB_DEF_FGCOLOR );
  982.                     lpKeyB->rgbBkColor  = GetNearestColor( hDC, KEYB_DEF_BKCOLOR );
  983.                     lpKeyB->wMsgDown    = WM_MIDIKEYBKEYDOWN;
  984.                     lpKeyB->wMsgUp      = WM_MIDIKEYBKEYUP;
  985.  
  986.                     ReleaseDC( hWnd, hDC );
  987.  
  988.                     /* initialize key array */
  989.                     for ( i = 0; i < KEYB_MAX_KEYS; i++ )
  990.                     {
  991.                         lpKeyB->rgKeyBKeys[ i ].fNoteOn = FALSE;
  992.                         lpKeyB->rgKeyBKeys[ i ].bNote = midiKeyBNormalKey( (BYTE)i );
  993.                         lpKeyB->rgKeyBKeys[ i ].bMIDINote = (BYTE)i;
  994.                     }
  995.  
  996.                     SendMessage( hWnd, KEYB_SETLAYOUT, FALSE,
  997.                             MAKELONG( KEYB_DEF_NUMKEYS, KEYB_DEF_FIRSTKEY ) );
  998.  
  999.                     /* unlock the control block */
  1000.                     GlobalUnlock( hKeyB );
  1001.  
  1002.                     /* go to DefWindowProc() to finish the job */
  1003.                     break;
  1004.                 }
  1005.  
  1006.                 /* free the memory (why couldn't I lock it???) */
  1007.                 else GlobalFree( hKeyB );
  1008.             }
  1009.  
  1010.             /* uh-oh! problems with memory, so fail! */
  1011.             return ( 0L );
  1012.         break;
  1013.  
  1014.  
  1015.         case WM_DESTROY:
  1016.             /* get rid of the control's hunk 'o junk */
  1017.             if ( hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB ) )
  1018.                 GlobalFree( hKeyB );
  1019.         break;
  1020.  
  1021.  
  1022.         case WM_ERASEBKGND:
  1023.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1024.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1025.             {
  1026.                 HBRUSH  hNewBk = CreateSolidBrush( lpKeyB->rgbBkColor );
  1027.                 HBRUSH  hOldBk;
  1028.  
  1029.                 GlobalUnlock( hKeyB );
  1030.  
  1031.                 if ( !hNewBk )
  1032.                     break;
  1033.  
  1034.                 hOldBk = SetClassWord( hWnd, GCW_HBRBACKGROUND, hNewBk );
  1035.                 lParam = DefWindowProc( hWnd, wMsg, wParam, lParam );
  1036.                 SetClassWord( hWnd, GCW_HBRBACKGROUND, hOldBk );
  1037.                 DeleteObject( hNewBk );
  1038.                 return ( lParam );
  1039.             }
  1040.         break;
  1041.  
  1042.  
  1043.         case WM_SIZE:
  1044.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1045.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1046.             {
  1047.                 lpKeyB->wWidth = LOWORD( lParam );
  1048.                 lpKeyB->wHeight = HIWORD( lParam );
  1049.                 midiKeyBSize( hWnd, lpKeyB );
  1050.                 GlobalUnlock( hKeyB );
  1051.             }
  1052.         break;
  1053.  
  1054.  
  1055.         case WM_RBUTTONDOWN:
  1056.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1057.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1058.             {
  1059.                 bNote = midiKeyBGetNote( lpKeyB, lParam );
  1060.                 if ( bNote != 0xFF )
  1061.                 {
  1062.                     /* toggle the key */
  1063.                     if ( lpKeyB->rgKeyBKeys[ bNote ].fNoteOn )
  1064.                     {
  1065.                         midiKeyBNoteOff( hWnd, bNote );
  1066.                     }
  1067.  
  1068.                     else
  1069.                     {
  1070.                         midiKeyBNoteOn( hWnd, bNote );
  1071.                     }
  1072.  
  1073.                     midiKeyBNoteChanged( hWnd, bNote );
  1074.                 }
  1075.  
  1076.                 GlobalUnlock( hKeyB );
  1077.             }
  1078.         break;
  1079.  
  1080.         case WM_LBUTTONDOWN:
  1081.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1082.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1083.             {
  1084.                 bNote = midiKeyBGetNote( lpKeyB, lParam );
  1085.                 if ( bNote != 0xFF )
  1086.                 {
  1087.                     midiKeyBNoteOn( hWnd, bNote );
  1088.  
  1089. keybLButtonDone:
  1090.                     midiKeyBNoteChanged( hWnd, bNote );
  1091.                 }
  1092.                 GlobalUnlock( hKeyB );
  1093.             }
  1094.         break;
  1095.  
  1096.         case WM_LBUTTONUP:
  1097.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1098.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1099.             {
  1100.                 bNote = midiKeyBGetNote( lpKeyB, lParam );
  1101.                 if ( bNote != 0xFF )
  1102.                 {
  1103.                     midiKeyBNoteOff( hWnd, bNote );
  1104.                     goto keybLButtonDone;
  1105.                 }
  1106.             }
  1107.         break;
  1108.  
  1109.  
  1110.         /*  It would be really neat if the keyboard would allow CTRL
  1111.          *  and SHIFT key modifiers like a multi-select list box.  The
  1112.          *  following code was used to slide the keyboard up and down
  1113.          *  in MIDI note messages--it is no longer used and probably
  1114.          *  does not work.
  1115.          */
  1116. #if 0
  1117.         case WM_KEYDOWN:
  1118.         {
  1119.             RECT        rc;
  1120.  
  1121.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1122.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1123.             {
  1124.                 GetWindowRect( hWnd, &rc );
  1125.                 rc.right = rc.right - rc.left;
  1126.                 rc.left = 0;
  1127.                 rc.top = lpKeyB->wHeight - (2 * lpKeyB->wTMHeight);
  1128.                 rc.bottom = lpKeyB->wHeight - lpKeyB->wTMHeight;
  1129.                 if ( wParam == VK_CONTROL )
  1130.                 {
  1131.                     lpKeyB->wKeyAdd = -lpKeyB->bNumKeys;
  1132.                     if ( !(lParam & 0x40000000) )
  1133.                         InvalidateRect( hWnd, &rc, TRUE );
  1134.                 }
  1135.  
  1136.                 else if ( wParam == VK_SHIFT )
  1137.                 {
  1138.                     lpKeyB->wKeyAdd = lpKeyB->bNumKeys;
  1139.                     if ( !(lParam & 0x40000000) )
  1140.                         InvalidateRect( hWnd, &rc, TRUE );
  1141.                 }
  1142.                 GlobalUnlock( hKeyB );
  1143.             }
  1144.         }
  1145.         break;
  1146.  
  1147.  
  1148.         case WM_KEYUP:
  1149.         {
  1150.             RECT        rc;
  1151.  
  1152.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1153.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1154.             {
  1155.                 GetWindowRect( hWnd, &rc );
  1156.                 rc.right = rc.right - rc.left;
  1157.                 rc.left = 0;
  1158.                 rc.top = lpKeyB->wHeight - (2 * lpKeyB->wTMHeight);
  1159.                 rc.bottom = lpKeyB->wHeight - lpKeyB->wTMHeight;
  1160.                 lpKeyB->wKeyAdd = 0;
  1161.                 InvalidateRect( hWnd, &rc, TRUE );
  1162.                 GlobalUnlock( hKeyB );
  1163.             }
  1164.         }
  1165.         break;
  1166. #endif
  1167.  
  1168.         case WM_MOUSEMOVE:
  1169.             if ( wParam & MK_LBUTTON )
  1170.             {
  1171.                 hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1172.                 if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1173.                 {
  1174.                     bNote = midiKeyBGetNote( lpKeyB, lParam );
  1175.                     if ( (bNote != 0xFF) && ( bNote != lpKeyB->bLastNote ) )
  1176.                     {
  1177.                         midiKeyBNoteOff( hWnd, lpKeyB->bLastNote );
  1178.                         midiKeyBNoteChanged( hWnd, lpKeyB->bLastNote );
  1179.  
  1180.                         midiKeyBNoteOn( hWnd, bNote );
  1181.                         midiKeyBNoteChanged( hWnd, bNote );
  1182.                     }
  1183.                     GlobalUnlock( hKeyB );
  1184.                 }
  1185.             }
  1186.         break;
  1187.  
  1188.  
  1189.         case WM_PAINT:
  1190.             BeginPaint( hWnd, &ps );
  1191.             midiKeyBPaint( hWnd, ps.hdc );
  1192.             EndPaint( hWnd, &ps );
  1193.             return ( 0L );
  1194.  
  1195.  
  1196.         case KEYB_GETMSGDOWN:
  1197.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1198.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1199.             {
  1200.                 wParam = lpKeyB->wMsgDown;
  1201.  
  1202. keybReturnwParam:
  1203.                 GlobalUnlock( hKeyB );
  1204.                 return ( (LONG)wParam );
  1205.             }
  1206.             return ( 0L );
  1207.  
  1208.         case KEYB_GETMSGUP:
  1209.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1210.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1211.             {
  1212.                 wParam = lpKeyB->wMsgUp;
  1213.                 goto keybReturnwParam;
  1214.             }
  1215.             return ( 0L );
  1216.  
  1217.         case KEYB_GETVELOCITY:
  1218.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1219.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1220.             {
  1221.                 wParam = (WORD)lpKeyB->bVelocity;
  1222.                 goto keybReturnwParam;
  1223.             }
  1224.             return ( 0L );
  1225.  
  1226.         case KEYB_GETCHANNEL:
  1227.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1228.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1229.             {
  1230.                 wParam = (WORD)lpKeyB->bChannel;
  1231.                 goto keybReturnwParam;
  1232.             }
  1233.             return ( 0L );
  1234.  
  1235.         case KEYB_GETHWND:
  1236.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1237.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1238.             {
  1239.                 wParam = (WORD)lpKeyB->hWnd;
  1240.                 goto keybReturnwParam;
  1241.             }
  1242.             return ( 0L );
  1243.  
  1244.         case KEYB_GETHMIDIOUT:
  1245.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1246.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1247.             {
  1248.                 wParam = lpKeyB->hMidiOut;
  1249.                 goto keybReturnwParam;
  1250.             }
  1251.             return ( 0L );
  1252.  
  1253.  
  1254.  
  1255.         case KEYB_SETMSGUP:
  1256.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1257.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1258.             {
  1259.                 lpKeyB->wMsgUp = wParam;
  1260. keybReturn0L:
  1261.                 GlobalUnlock( hKeyB );
  1262.             }
  1263.             return ( 0L );
  1264.  
  1265.         case KEYB_SETMSGDOWN:
  1266.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1267.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1268.             {
  1269.                 lpKeyB->wMsgDown = wParam;
  1270.                 goto keybReturn0L;
  1271.             }
  1272.             return ( 0L );
  1273.  
  1274.         case KEYB_SETCHANNEL:
  1275.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1276.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1277.             {
  1278.                 lpKeyB->bChannel = (BYTE)wParam;
  1279.                 goto keybReturn0L;
  1280.             }
  1281.             return ( 0L );
  1282.  
  1283.         case KEYB_SETVELOCITY:
  1284.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1285.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1286.             {
  1287.                 lpKeyB->bVelocity = (BYTE)wParam;
  1288.                 goto keybReturn0L;
  1289.             }
  1290.             return ( 0L );
  1291.  
  1292.         case KEYB_SETHWND:
  1293.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1294.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1295.             {
  1296.                 lpKeyB->hWnd = wParam;
  1297.                 goto keybReturn0L;
  1298.             }
  1299.             return ( 0L );
  1300.  
  1301.         case KEYB_SETHMIDIOUT:
  1302.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1303.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1304.             {
  1305.                 lpKeyB->hMidiOut = wParam;
  1306.                 goto keybReturn0L;
  1307.             }
  1308.             return ( 0L );
  1309.  
  1310.  
  1311.  
  1312.         case KEYB_GETBKCOLOR:
  1313.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1314.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1315.             {
  1316.                 lParam = lpKeyB->rgbFgColor;
  1317.  
  1318. keybReturnlParam:
  1319.                 GlobalUnlock( hKeyB );
  1320.                 return ( lParam );
  1321.             }
  1322.             return ( 0L );
  1323.  
  1324.         case KEYB_GETFGCOLOR:
  1325.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1326.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1327.             {
  1328.                 lParam = lpKeyB->rgbBkColor;
  1329.                 goto keybReturnlParam;
  1330.             }
  1331.             return ( 0L );
  1332.  
  1333.  
  1334.         case KEYB_SETBKCOLOR:
  1335.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1336.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1337.             {
  1338.                 HDC hDC = GetDC( hWnd );
  1339.                 lpKeyB->rgbBkColor = GetNearestColor( hDC, lParam );
  1340.                 ReleaseDC( hWnd, hDC );
  1341.                 GlobalUnlock( hKeyB );
  1342.             }
  1343.  
  1344.             goto keybRepaintTheThing;
  1345.  
  1346.         case KEYB_SETFGCOLOR:
  1347.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1348.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1349.             {
  1350.                 HDC hDC = GetDC( hWnd );
  1351.                 lpKeyB->rgbFgColor = GetNearestColor( hDC, lParam );
  1352.                 ReleaseDC( hWnd, hDC );
  1353.                 GlobalUnlock( hKeyB );
  1354.             }
  1355.  
  1356.             goto keybRepaintTheThing;
  1357.  
  1358.  
  1359.         case KEYB_GETLAYOUT:
  1360.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1361.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1362.             {
  1363.                 lParam = MAKELONG( lpKeyB->bNumKeys, lpKeyB->bFirstKey );
  1364.                 GlobalUnlock( hKeyB );
  1365.             }
  1366.  
  1367.             else lParam = 0L;
  1368.  
  1369.             /* return the current number of keys and starting number */
  1370.             return ( lParam );
  1371.  
  1372.  
  1373.         case KEYB_SETLAYOUT:
  1374.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1375.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1376.             {
  1377.                 lpKeyB->bNumKeys = (BYTE)LOWORD( lParam );
  1378.                 lpKeyB->bFirstKey = (BYTE)HIWORD( lParam );
  1379.                 midiKeyBSize( hWnd, lpKeyB );
  1380.                 GlobalUnlock( hKeyB );
  1381.             }
  1382.  
  1383. keybRepaintTheThing:
  1384.             /* redraw if indicated in message */
  1385.             if ( (BOOL)wParam )
  1386.             {
  1387.                 InvalidateRect( hWnd, NULL, TRUE );
  1388.                 UpdateWindow( hWnd );
  1389.             }
  1390.             return ( 0L );
  1391.  
  1392.  
  1393.         case KEYB_MIDISHORTMSG:
  1394.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1395.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1396.             {
  1397.                 /* decode the short message */
  1398.                 midiKeyBShortMsg( hWnd, lpKeyB, lParam );
  1399.  
  1400.                 GlobalUnlock( hKeyB );
  1401.  
  1402.                 /* repaint the WHOLE THING if wParam is non-zero */
  1403.                 goto keybRepaintTheThing;
  1404.             }
  1405.             return ( 0L );
  1406.  
  1407.  
  1408.         case KEYB_RESET:
  1409.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1410.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1411.             {
  1412.                 short   i;
  1413.  
  1414.                 /* set all key states to off */
  1415.                 for ( i = 0; i < KEYB_MAX_KEYS; i++ )
  1416.                 {
  1417.                     lpKeyB->rgKeyBKeys[ i ].fNoteOn = FALSE;
  1418.                 }
  1419.  
  1420.                 GlobalUnlock( hKeyB );
  1421.  
  1422.                 /* repaint the WHOLE THING if wParam is non-zero */
  1423.                 goto keybRepaintTheThing;
  1424.             }
  1425.             return ( 0L );
  1426.  
  1427.  
  1428.         case WM_GETFONT:
  1429.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1430.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1431.             {
  1432.                 hFont = lpKeyB->hFont;
  1433.                 GlobalUnlock( hKeyB );
  1434.             }
  1435.  
  1436.             else hFont = NULL;
  1437.  
  1438.             /* if system font, then return NULL handle */
  1439.             return ( (hFont == GetStockObject(SYSTEM_FONT)) ? NULL : hFont );
  1440.  
  1441.         case WM_SETFONT:
  1442.             /* if NULL hFont, use system font */
  1443.             if ( !(hFont = (HFONT)wParam) )
  1444.                 hFont = GetStockObject( SYSTEM_FONT );
  1445.  
  1446.             hKeyB = GetWindowWord( hWnd, KEYB_WW_HMIDIKEYB );
  1447.             if ( lpKeyB = (LPMIDIKEYB)GlobalLock( hKeyB ) )
  1448.             {
  1449.                 HDC         hDC;
  1450.                 TEXTMETRIC  tm;
  1451.  
  1452.                 /* grab the text metrics */
  1453.                 hDC = GetDC( hWnd );
  1454.                 if ( lpKeyB->hFont = hFont )
  1455.                     SelectObject( hDC, hFont );
  1456.                 GetTextMetrics( hDC, &tm );
  1457.                 ReleaseDC( hWnd, hDC );
  1458.  
  1459.                 /* fill control block with defaults */
  1460.                 lpKeyB->wTMHeight   = tm.tmHeight;
  1461.                 lpKeyB->wTMAvgWidth = tm.tmAveCharWidth;
  1462.  
  1463.                 GlobalUnlock( hKeyB );
  1464.             }
  1465.  
  1466.             /* set wParam to lParam BOOL so it will repaint appropriately */
  1467.             wParam = (WORD)lParam;
  1468.             goto keybRepaintTheThing;
  1469.  
  1470.     } /* switch () */
  1471.  
  1472.     /* let the dialog mangler take care of this message */
  1473.     return ( DefWindowProc( hWnd, wMsg, wParam, lParam ) );
  1474. } /* midiKeyBProc() */
  1475.  
  1476.  
  1477. /** EOF: midikeyb.c **/
  1478.